
#include <stdio.h>
#include <utils_ptrlist.h>
#ifdef linux
#include <netdb.h>
#include <pthread.h> 
#include <utils_net.h>
#include <longtime.h>
#include <utils_daemon.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/select.h>
#else
#include <windows.h>
#include <io.h> 
#endif
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h> 
#include "utils_ptrlist.h"
#include "ts_thread.h"
#include "../common/winnet.h"
#include "../common/cJSON.h"

#if _WIN32_WINNT >= 0x0600 && (defined ENABLE_NETINFO_API || defined ENABLE_PING)
#include <iphlpapi.h>
#pragma comment(lib, "IPHLPAPI.lib")
#endif

#define max(a, b) (a > b ? a : b)
#ifdef linux
#define Sleep(n) usleep(n * 1000)
#endif
int has_gone = 0;
void trace_log(const char *fmt, ...);
void lprintf(const char *fmt, ...);

#define trace_log printf

int json_get_bool_safe(cJSON *cf, const char *key, int defval)
{
	cJSON *ele;
	const char *strval;
	ele = cJSON_GetObjectItem(cf, key);
	if (!ele)
		return defval;
	if (ele->type == cJSON_True)
		return 1;
	if (ele->type == cJSON_False)
		return 0;
	if (ele->type == cJSON_String)
	{
		strval = ele->valuestring;
		if (strncmp(strval, "1", 1) == 0 ||
			strncmp(strval, "yes", 3) == 0 ||
			strncmp(strval, "on", 2) == 0 ||
			strncmp(strval, "enable", 6) == 0 ||
			strncmp(strval, "en", 2) == 0)
			return 1;
		else
			return 0;
	}
	if (ele->type == cJSON_Number)
		return ele->valueint > 0 ? 1 : 0;
	return defval;
}

int json_get_int_safe(cJSON *cf, const char *key, int defval)
{
	int val;
	cJSON *ele;
	const char *strval;
	ele = cJSON_GetObjectItem(cf, key);
	if (ele && ele->type == cJSON_Number)
		return ele->valueint;
	if (ele && ele->type == cJSON_String)
		return atoi(ele->valuestring);
	return defval;
}

const char *json_get_string_safe(cJSON *cf, const char *key, const char *defval)
{
	char number[200] = { 0 };
	cJSON *ele;
	const char *strval;
	ele = cJSON_GetObjectItem(cf, key);
	if (!ele || ele->type != cJSON_String)
		return defval;
	return ele->valuestring;
}

typedef struct tagProxy
{
	int enable;
	int sock_dev;
	int sock_cloud;
	int device;
} ProxyNode;

static int bQuit = 0;
static ts_thread_t proxy;
static PtrList ClientList = PTRLIST_INITIALIZER;			 // 客户队列
static ts_mutex_t client_lock; // 客户队列操作锁，因为客户维护都是在该进程中操作的也可以不要

#define CLIENT_LOCK() ts_mutex_lock(&client_lock)
#define CLIENT_UNLOCK() ts_mutex_unlock(&client_lock)

// 转发线程，我们将架线tcp数据另外开设线程，防止主线程控制方式的阻塞调用耽误数据的转发
void *forward_thread(void *arg)
{
	//ProxyNode *obj = NULL;
	int sock_dev, sock_cloud;
	POSITION pos;
	int fd_max = 0;
	char buffer[4096] = { 0 };
	while (!bQuit)
	{	
		// TOOD client has gone, delete..
		if (has_gone == 1)
		{
			CLIENT_LOCK();
			for (pos = ClientList.head; pos != NULL; pos = pos->next)
			{
				ProxyNode *obj = (ProxyNode *)pos->ptr;
				if (obj->enable == 0)
				{
					obj = PtrList_remove(&ClientList, pos);
					if (obj->sock_cloud > 0)
						close(obj->sock_cloud);
					if (obj->sock_dev > 0)
						close(obj->sock_dev);
					free(obj);
					trace_log("delete sock pair ..\r\n");
					pos = ClientList.head;
					if (pos == NULL)
						break;
				}
			}
			CLIENT_UNLOCK();
		}

		if (ClientList.count == 0)
		{
			Sleep(10);
			continue;
		}
		fd_set fs;
		struct timeval tv;
		int ret;
		tv.tv_sec = 1;
		tv.tv_usec = 0;
		FD_ZERO(&fs);
		CLIENT_LOCK();
		for (pos = ClientList.head; pos != NULL; pos = pos->next)
		{
			ProxyNode *obj = (ProxyNode *)pos->ptr;
			if (!obj->enable)
				continue;
			fd_max = fd_max > obj->sock_dev ? fd_max : obj->sock_dev;
			FD_SET(obj->sock_dev, &fs);
			fd_max = fd_max > obj->sock_cloud ? fd_max : obj->sock_cloud;
			FD_SET(obj->sock_cloud, &fs);
		}
		CLIENT_UNLOCK();
		ret = select(fd_max + 1, &fs, NULL, NULL, &tv);
		has_gone = 0;
		if (ret > 0)
		{
			for (pos = ClientList.head; pos != NULL; pos = pos->next)
			{
				ProxyNode *obj = (ProxyNode *)pos->ptr;
				if (!obj->enable)
					continue;
				if (obj->sock_dev > 0 && FD_ISSET(obj->sock_dev, &fs))
				{
					int len = sock_read(obj->sock_dev, (char *)buffer, sizeof(buffer));
					if (len <= 0)
					{
						sock_close(obj->sock_dev);
						obj->sock_dev = -1;
						sock_close(obj->sock_cloud);
						obj->sock_cloud = -1;
						trace_log("连接已经断开..\r\n");
						obj->enable = 0;
						has_gone = 1;
						continue;
					}
					if (obj->sock_cloud > 0)
						sock_write_n_bytes(obj->sock_cloud, buffer, len);
				}
				if (obj->sock_cloud > 0 && FD_ISSET(obj->sock_cloud, &fs))
				{
					int len = sock_read(obj->sock_cloud, (char *)buffer, sizeof(buffer));
					if (len <= 0)
					{
						trace_log("连接已经断开..\r\n");
						sock_close(obj->sock_dev);
						obj->sock_dev = -1;
						sock_close(obj->sock_cloud);
						obj->sock_cloud = -1;
						obj->enable = 0;
						has_gone = 1;
						continue;
					} 
					sock_write_n_bytes(obj->sock_dev, buffer, len);
				}
			}
		} 
	}
	return NULL;
} 

int Dev_open_line(const char *server_ip, int server_port, const char *bridge_ip, int bridge_port)
{
	int fd_br, fd_serv;
	fd_br = sock_connect(bridge_ip, bridge_port);
	if (fd_br < 0)
	{
		trace_log("connect to bridge_ip server:%s at port:%d error\r\n", bridge_ip, bridge_port);
		return -1;
	}
	else
	{
		trace_log("connect to bridge_ip :%s at port:%d success \r\n", bridge_ip, bridge_port);
	}
	fd_serv = sock_connect(server_ip, server_port);
	if (fd_serv < 0)
	{
		close(fd_br);
		fd_br = -1;
		trace_log("connect to server_ip server:%s at port:%d error\r\n", server_ip, server_port);
		return -1;
	}
	else
	{
		trace_log("connect to server_ip :%s at port:%d success \r\n", server_ip, server_port);
	}
	ProxyNode *node = malloc(sizeof(ProxyNode));
	memset(node, 0, sizeof(ProxyNode));
	node->sock_dev = fd_br;
	node->sock_cloud = fd_serv;
	node->enable = 1;
	trace_log("架线成功!\r\n");
	CLIENT_LOCK();
	PtrList_append(&ClientList, node);
	CLIENT_UNLOCK();
	return 0;
}

void Dev_deinit()
{
	bQuit = 1;
	ts_thread_terminate(&proxy);
}

void Dev_init()
{
	ts_thread_create(&proxy, NULL, forward_thread, NULL);
}
